Spectrum Virtualize RESTful API
Spectrum Virtualize 具象状态传输 (REST) 模型应用程序编程接口 (API) 由用于检索系统信息以及用于创建、修改和删除系统资源的命令目标组成。这些命令目标允许命令参数未经编辑地传递到 Spectrum Virtualize 命令行界面,由该界面处理参数规范有效性的解析和错误报告。 使用安全超文本传输协议 (HTTPS) 可成功与 RESTful API 服务器进行通信。
RESTful API 服务器不考虑传输安全性(例如 SSL),而是假设请求是从本地受保护服务器发起的。 HTTPS 协议通过数据加密来提供隐私。 RESTful API 通过要求进行命令认证来提供更多安全性(持续两小时活动或 30 分钟不活动,以先发生的情况为准)。
统一资源定位符 (URL) 锁定系统上的不同节点对象。HTTPS POST 方法处理 URL 中指定的命令目标。有关更多信息,请参阅RESTful API 命令目标和特征。要进行更改或查看有关系统上不同对象的信息,必须创建请求并将其发送到系统。 您需要提供特定元素以使 RESTful API 服务器接收请求并将其转换为命令,如下一节中所述。
生成 HTTPS 请求
https://system_node_ip:7443/rest/command
其中:所有命令(包括 LS 命令)都至少接受一个命名参数。 由于 Spectrum Virtualize RESTful API 只是 POST 方法,因此其实施的约定是将位置参数附加到 URI,然后将命名参数作为 JavaScript 对象表示法 (JSON) 字符串打包到请求主体中。
命令目标的更简练方法是专门采用 POST。
如上所述,除命令目标的 URL 和名称以外,在请求行中和 HTTP 请求主体中,还需要有关对指定对象所执行操作的其他信息。在请求行中,包含 POST HTTP 方法。 在请求主体中包含任何必需参数(例如 RAID 级别或 IP 地址)。
{'X-Auth-Username': 'superuser'}HTTP/1.1 200 OK
Server: lighttpd/1.4.31
Date: date
Content-type: application/json; charset=UTF-8
Content-length: content_length
Connection: close
{"attribute": "value"}要查看 API 命令目标及其特征,请参阅 RESTful API 命令目标和特征。 要查看如何入门的示例,请参阅入门。 请参阅 RESTful API HTTP 错误消息,以获取可能遇到的 HTTP 错误代码的完整列表。
RESTful API 命令目标和特征
表 1 强调所有命令(包括 /auth)的 POST 方法。它还显示您必须使用 /auth 命令目标返回的认证令牌来认证您运行的每个其他命令。除 /auth 命令目标外,针对系统 IP 地址运行命令,以便由配置节点运行这些命令。
| 命令目标 | 方法 | 需要认证 | 在配置节点/集群中运行 |
|---|---|---|---|
| /auth | POST | 否 | 否 |
| 所有其他命令目标 | POST | 是 | 是 |
可在产品文档的“CLI 命令”部分中获取命令目标及其参数的描述,以及其他不太常用命令的描述。
| 命令目标 | ||
|---|---|---|
| /addhostclustermember | /addhostiogrp | /addhostport |
| /addvdiskaccess | /addvdiskcopy | /addvolumecopy |
| /auth | /chhost | /chnode |
| /chnodecanister | /chrcconsistgrp | /chrcrelationship |
| /chvdisk | /expandvdisksize | /lscurrentuser |
| /lseventlog | /lsfcconsistgrp | /lsfcmap |
| /lsfcmapcandidate | /lsfcmapdependentmaps | /lsfcmapprogress |
| /lshost | /lshostcluster | /lshostclustermember |
| /lshostclustervolumemap | /lshostiogrp | /lshostvdiskmap |
| /lsiogrp | /lsiogrphost | /lsmdiskgrp |
| /lsnode | /lsnodecanister | /lsnodehw |
| /lsnodecanisterhw | /lsnodecanisterstats | /lsnodecanistervpd |
| /lsnodehw | /lsnodestats | /lsnodevpd |
| /lspartnership | /lsrcrelationshipprogress | /lssystem |
| /lssystemip | /lssystemstats | /lsvdisk |
| /lsvdiskaccess | /lsvdiskcopy | /lsvdiskfcmapcopies |
| /lsvdiskfcmappings | /lsvdiskhostmap | /lsvdisksyncprogress |
| /mkfcconsistgrp | /mkfcmap | /mkfcpartnership |
| /mkhost | /mkhostcluster | /mkrcconsistgrp |
| /mkrcrelationship | /mkvdisk | /mkvdiskhostmap |
| /mkvolume | /mkvolumehostclustermap | /movevdisk |
| /prestartfcconsistgrp | /prestartfcmap | /rmfcmap |
| /rmhost | /rmhostcluster | /rmhostclustermember |
| /rmhostiogrp | /rmhostport | /rmvdisk |
| /rmvdiskaccess | /rmvdiskcopy | /rmvdiskhostmap |
| /rmvolume | /rmvolumecopy | /rmvolumehostclustermap |
| /startfcmap | /startrcconsistgrp | /startrcrelationship |
| /stopfcconsistgrp | /stopfcmap | /stoprcconsistgrp |
| /stoprcrelationship | ||
认证概述
除数据加密外,HTTPS 服务器还需要认证每个 API 会话的有效用户名和密码。使用两个认证头字段指定凭证:X-Auth-Username 和 X-Auth-Password。
初始认证需要使用用户名和密码对认证目标 (/auth) 执行 POST 操作。RESTful API 服务器会返回十六进制令牌。 单个会话最多持续两小时处于活动状态或 30 分钟处于不活动状态,以先发生的情况为准。 当会话由于不活动而结束时,或者如果达到分配的最长时间,那么错误代码 403 指示失去授权。使用 /auth 命令目标通过用户名和密码重新认证。
https://192.168.10.109:7443/rest/auth, method="POST",
headers={'X-Auth-Username': 'superuser', 'X-Auth-Password': 'passw0rd'}发送到 API 服务器的 HTTP 请求如下所示:POST /auth HTTPS/HTTPS_version
Host: https://192.168.10.109:7443
Content-type: application/json; charset=UTF-8
Content-length: message_size
X-Auth-Token: 58cfd6acb1676e1cba78b7cb5a9a081d11d1d1cfeb0078083ef225d9c59bf4df
{"attribute": "value","attribute": "value"}其中:- 第一行是包含 POST 方法、API 目标、协议 (HTTPS) 和协议版本 (1.1) 的请求行。
- 第二行是主机头,将 HTTP 请求定向到系统上的正确端口 (7443) 和 IP 地址。
- 第三行是内容类型头,用于指定内容类型 (application/json; charset=UTF-8)。
- 第四行是具有消息大小的内容长度头。
- 第五行是具有认证令牌的认证令牌头。
- 请求的头和主体之间留有空格。 任何参数都出现在第七行上的 JSON 中。
{
"token": "58cfd6acb1676e1cba78b7cb5a9a081d11d1d1cfeb0078083ef225d9c59bf4df"
}命令目标参数
入门
以下 Python 3 示例显示如何完成初始设置以开始与系统交互并运行命令。有关其他语言的示例,请参阅 Perl 中的 RESTful API 用法示例和 CURL 中的用法示例。
import ssl
import json
import pprint
import urllib.request
import urllib.error
import urllib.parse
no_verify = ssl.create_default_context()
no_verify.check_hostname = False
no_verify.verify_mode = ssl.CERT_NONE
if getattr(ssl, '_https_verify_certificates', None):
ssl._https_verify_certificates(False)
class HostString(str):
"""
Comment: Special subclass of string, for storing arbitrary host-related
attributes (such as auth tokens) without losing any string behavior
"""
def __new__(cls, *args, **kwds):
return super(HostString, cls).__new__(cls, *args, **kwds)
class RESTUtil(object):
show_default=False
default_headers = {}
port = 80
def __init__(self, show=None, catch=True):
self.hosts = {}
self.curr_host = None
self.catch=catch
self.show_default=show if show != None else self.show_default
@property
def host(self):
return self.curr_host
@host.setter
def host(self, hostname):
"""
Comment: Retrieve the HostString object of a known host from its
host name or string definition. Even if the host definition
is provided, we still need to key into self.hosts in case the
client classes are storing things on their HostString objects.
"""
try:
if hostname in self.hosts:
self.curr_host = self.hosts[hostname]
else:
self.curr_host = [h for h in self.hosts.values() if h == hostname][0]
return self.curr_host
except IndexError:
raise KeyError("Unrecognized host/name %s" % hostname)
def add_host(self, hostdef, hostname=None):
hostname = hostname if hostname is None else hostdef
self.hosts[hostname] = HostString(hostdef)
if self.curr_host == None:
self.curr_host = self.hosts[hostname]
return hostname
def command(self, protocol, postfix, method='POST', headers=None, show=None, **cmd_kwds):
"""
Comment: A fairly generic RESTful API request builder.
See subclasses for examples of use.
"""
if show == None:
show = self.show_default
headers = {} if headers == None else headers
url = '%s://%s:%s/%s' % (
protocol,
self.curr_host,
self.port,
postfix
)
request = urllib.request.Request(
url,
headers =dict(self.default_headers, **headers),
data=bytes(json.dumps(cmd_kwds), encoding="utf-8") if cmd_kwds else None)
request.get_method = lambda: method
if show:
self.request_pprint(request)
try:
cmd_out = urllib.request.urlopen(request, context=no_verify).read().decode('utf-8')
except urllib.error.HTTPError as e:
self.exception_pprint(e)
if not self.catch:
raise Exception("RESTful API command failed.")
return
try:
cmd_out = json.loads(cmd_out)
except ValueError:
pass
if show:
print("\nCommand Output:")
pprint.pprint(cmd_out)
print("")
return cmd_out
@staticmethod
def request_pprint(request):
"""
Comment: Request info print function
(for self.command with show=True)
"""
print(request.get_method(), request.get_full_url(), 'HTTP/1.1')
print('Host:', request.host)
for key, value in request.headers.items():
print(key.upper() + ':', str(value))
if request.data != None:
print()
pprint.pprint(request.data)
@staticmethod
def exception_pprint(http_error):
"""
Comment: HTTPError info print function
"""
print(http_error.code, '--', http_error.reason)
print(http_error.fp.read())
print("")
class SVCREST(RESTUtil):
"""
Comment: RESTful wrapper for the SVC CLI
"""
def __init__(self, host, *args, **kwds):
self.debug = kwds.pop('debug', False)
super().__init__(*args, **kwds)
self.add_host(host)
@property
def default_headers(self):
return {'X-Auth-Token': getattr(self.curr_host, 'token', 'badtoken'),
'Content-Type': 'application/json'}
@property
def port(self):
return getattr(self, '_port', None) or ('7665' if self.debug else '7443')
@property
def protocol(self):
return getattr(self, '_protocol', None) or ('http' if self.debug else 'https')
def command(self, cmd, *args, method="POST", headers=None, show=None, **cmd_kwds):
postfix = '/'.join(
['rest'] + [cmd] + [urllib.parse.quote(str(a)) for a in args]
)
return super().command(
self.protocol,
postfix,
method=method,
headers=headers,
show=show,
**cmd_kwds
)
def authenticate(self, username='superuser', password='passw0rd', show=None):
cmd_out = self.command(
'auth', show=show, method="POST", headers={'X-Auth-Username': username, 'X-Auth-Password': password}
)
if cmd_out:
self.curr_host.token = cmd_out['token']
"""
Comment: First, set your cluster ipaddress.
It's assumed superuser/passw0rd (6 lines above) is the crednetial.
After the authenticate call, you can issue any command in
s.command('') that is an svcinfo or svctask cmmand)
"""
s = SVCREST('192.168.10.109')
s.authenticate()
print(s.command('lssystem'))